
/**
    Converts a menu-structed object to an unordered list (UL) of
    items, in a form usable as a basic tree-like menu.

    The returned object is a jQuery object holding
    a UL which presents a simple colapsible menu
    based on the input model. The returned object is NOT
    in the DOM, and must be added to the DOM by the caller.

    This is intended for generating menus from a generic
    representation, rather than creating them directly
    in HTML (where the event handling and menu appearance
    are normally intertwined, which is really fugly,
    philosphically speaking).

    The general structure of a menu is:

    {
        title:"Label",
        icon:'image.png',
        onclick:function(menuElement,jqElement)
        {
            // menuElement == this menu entry object
            // jqElement == the selected menu entry's "view" object (an LI jQuery element)
        },
        entries[
            sub-menus (same structure) can be embedded to
            any depth but MUST NOT have cycles.
        ]
    }

    All of the elements of a menu are optional except for
    the title, which can be any type for which jQuery.append()
    is legal. Strangely enough, the title and icon of the outmost
    object is ignored (since it has no visual reprentation).
    In fact, the title can be empty, in which case a separator
    (HR element) is added in its place and any other properties
    of that element (including children) are ignored.

    The outer-most object may optionally contain an element
    called 'opt' which may contain the following key/value pairs,
    which are explained in more detail below:

    {
        speed:animationSpeed,
        classPrefix:string,
        dataKey:string,
        doFlash:boolean,
        ulCSS:{ jQuery.css()-compatible key/value pairs }
    }

    (They all have somewhat reasonable defaults.)

    The properties are:

    .speed is the speed for animations used by the generated menu. Any
    value supported by jQuery.fadeIn() (and friends) is supported.
    The default is "something reasonable", probably erring on the side of
    fast.

    .classPrefix is a string prefix applied to CSS classes used by this menu.
    Default='bogomenu-'

    The classes used are:

        PREFIX+'branch' = class for "branch" (non-leaf) items. i.e. those with children.

        PREFIX+'leaf' = class for "leaf" (non-branch) items. i.e. those without children.

        PREFIX+'separator' = class used for HR elements which act as separators.
        (Items with no title are treated as separators.)

        PREFIX+'menu' = the class for the outer-most UL object which represents
        the menu as a whole.

    .dataKey: each generated menu item stores the associated menu element
    object in jQuery.data() using this key.
    Default='bogomenu-menuitem'

    .doFlash: by default the menu flashes a selected item to make it clear
    what was clicked. Set this to false to disable that.

    .ulCSS: these CSS attributes are applied to all UL generated elements,
    from the outer-most container to the deepest sub-menus.


    Here is some sample CSS for use with the generated menu, which demonstrates
    how to add, e.g. hover effects to the menu entries:


    ul.bogomenu-menu // main menu view
    {
        background-color: white;
        border: 2px inset black;
    }


    span.bogomenu-branch // menu entry with children
    {
        font-size: 0.9em;
    }
    span.bogomenu-branch:hover // menu entry w/ children, hovering
    {
        color: white;
        background-color: darkgreen;
    }
    span.bogomenu-leaf // menu entry w/o children
    {
        font-size: 0.8em;
        font-style: italic;
    }
    span.bogomenu-leaf:hover // menu entry w/o children, hovering
    {
        color: white;
        background-color: darkgreen;
        border: 2px outset black;
    }

    EXTERNAL REQUIREMENTS:

    - jQuery. "Should" work with 1.2.x, but only tested with 1.3.x.

    - jquery.html.js, for the jQuery.html API.


    Author: Stephan Beal (http://wanderinghorse.net/home/stephan/)

    License: Public Domain, unless your jQuery is licensed as GPL,
    in which case this code is necessarily GPL. (That's how viral
    licenses work!)
*/
jQuery.extend({
    createBogoMenu:function(mobj)
    {
        var defopt = {
            speed:250,
            classPrefix:'bogomenu-',
            dataKey:'bogomenu-menuitem',
            doFlash:true,
            ulCSS:
            {
                'list-style-type':'none',
                //'list-style-position':'outside',
                'padding':'0px 0px 0px 0.5em',
                 'margin':'0px'
            }
        };
        var mopt = mobj.opt ? mobj.opt : {};
        var opt = jQuery.extend(true, defopt, mopt );
        function flashit(obj)
        {
            obj.fadeOut(opt.speed,function(){obj.fadeIn(opt.speed);});
        };
        /**
            ent must be-a menu entry object. tgt must be-a
            jQuery UL element out of which to create the menu.
            Recusively traverses ent and creates new HTML-side
            objects for it.
        */
        function process(tgt,ent)
        {
            var li = jQuery.html.li();
            li.data( opt.dataKey, ent );
            tgt.append(li);
            if( ! ent.title )
            {
                var hr = jQuery.html.hr();
                hr.addClass( opt.classPrefix+'separator' )
                    .appendTo(li);
                return li;
            }
            var s = jQuery.html.span();
            s//.attr('name',ent.title)
                .css('cursor','pointer')
                //.css('padding','0px 0px 0px 18px')// worka^H^H^H^H^Hkludge for icon alignment
                //.data( opt.dataKey, ent )
                ;
            if( ent.icon )
            {// tough decision here...
                //var img = jQuery.html.img();
                //img.attr('src', ent.icon);
                //s.append(img);
                //li.attr('list-type-image',ent.icon);
                s.css('background-image','url('+ent.icon+')')
                    .css('background-repeat','no-repeat')
                    .css('background-position','left')
                    .css('padding','0px 0px 0px 18px')// worka^H^H^H^H^Hkludge for icon alignment
                    ;
            }
            else
            {
                //s.append( jQuery.html.span().css('padding','0.5em').append('&nbsp;') );
            }
            s.append(ent.title).appendTo(li);
            if( ent.onclick )
            {
                s.click( function(ev){
                    if( opt.doFlash ) flashit(jQuery(this));
                    ent.onclick(ent,li);
                    ev.preventDefault();
                    return false;
                });
            }
            if( opt.doFlash )
            {
                s.click( function(){
                             flashit(jQuery(this));
                             return false;
                         } );
            }
            if(ent.entries)
            {
                var subul = jQuery.html.ul();
                subul.hide();
                subul.css(opt.ulCSS);
                li.append(subul);
                for( var i in ent.entries )
                {
                    process(subul,ent.entries[i]);
                }
                s.click(function(e){
                    //jQuery('ul',li).slideToggle(opt.speed);
                    subul.slideToggle(opt.speed);
                    //li.children('ul').toggle();
                    //jQuery('ul',li).toggle();
                    e.preventDefault();
                    return false;
                });
                //jQuery('ul',li).hide() /* not working for me on Chrome! */;
                s.addClass( opt.classPrefix+'branch' );
            }
            else
            {
                s.addClass( opt.classPrefix+'leaf' );
            }
            return li;
        };
        var ul = jQuery.html.ul();
        ul.css(opt.ulCSS)
            .addClass(opt.classPrefix+'menu');
        if(mobj.entries)
        {
            for( var i in mobj.entries )
            {
                process(ul,mobj.entries[i]);
            }
            //jQuery('ul',ul).hide() /* not working for me on Chrome! */;
        }
        return ul;
    }
});
